<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>来自于官网：https://x6.antv.vision/zh/examples/showcase/practices#orgchart</title>
  </title>
  <script src="./antvx6.js"></script>
  <!-- <script src="./insertcss.js"></script> -->
  <script src="./dagre.min.js"></script>
  <script src="./layout.min.js"></script>
  <style>
    .x6-cell {
      cursor: default;
    }

    /* 禁止连接线悬浮时的高亮 */
    .x6-cell.x6-edge .source-marker,
    .x6-cell.x6-edge .connection-wrap,
    .x6-cell.x6-edge .vertices,
    .x6-cell.x6-edge .arrowheads,
    .x6-cell.x6-edge .tools {
      display: none !important;
    }

    #clearGraph {
      position: fixed;
      z-index: 12;
    }
  </style>
</head>

<body>
  <!-- <svg viewBox="0 0 1137 1024" width="16" height="16">
    <path
      d="M805.88195 284.333055c0.340989-5.683149 0.795641-11.650455 1.307125-17.901919l4.035035-240.965514a25.744665 25.744665 0 0 1 16.651627-23.357742 25.290013 25.290013 0 0 1 27.676935 6.819779l276.030542 319.336137c5.683149 7.61542 6.706116 17.845088 2.67108 26.483474a25.460507 25.460507 0 0 1-21.993786 14.719355h-192.886074a42.850943 42.850943 0 0 1-10.456994 1.250293H43.032872A43.078269 43.078269 0 0 1 0.068267 327.524986c0-23.869225 19.209043-43.191932 42.964605-43.191931h762.849078z m-480.794397 370.20032h767.963912c23.698731 0 42.964606 19.322706 42.964605 43.191931s-19.265875 43.191932-42.964605 43.191932H330.827533l-1.250293 16.651626-4.035035 240.965514a25.744665 25.744665 0 0 1-16.651627 23.357742 25.290013 25.290013 0 0 1-27.676935-6.819779L5.183101 695.679373a25.971991 25.971991 0 0 1-2.67108-26.483474 25.460507 25.460507 0 0 1 21.993786-14.719356H325.144384z"
      fill="#414F58"></path>
  </svg> -->

  <button id="clearGraph" onclick="clearGraph()">清空画布</button>
  <div style="display: flex;width: 1600px;height:500px;border:1px solid #000;">
    <div id="container" style="flex: 1;width: 1600px; height: 500px;"></div>
  </div>
  <script>
    let graph;
    function clearGraph() {
      graph.dispose();
    }
    window.onload = function () {
      const Graph = X6.Graph;
      const Dom = X6.Dom;
      const BLOCK_WIDTH = 260;// 小块的宽度
      const BLOCK_HEIGHT = 114;// 小块的高度
      const BLOCK_PADDING = 10;// 小块的padding

      // 自定义节点
      Graph.registerNode(
        'org-node',
        {
          width: BLOCK_WIDTH,
          height: BLOCK_HEIGHT,
          markup: [
            {
              tagName: 'rect',
              attrs: {
                class: 'card',
              },
            },
            {
              tagName: 'text',
              attrs: {
                class: 'line_1',
              },
            },
            {
              tagName: 'text',
              attrs: {
                class: 'line_2',
              },
            },
            {
              tagName: 'text',
              attrs: {
                class: 'line_3',
              },
            },
            {
              tagName: 'text',
              attrs: {
                class: 'line_4',
              },
            },
            {
              tagName: 'g',
              attrs: {
                class: 'btn add',
              },
              children: [
                {
                  tagName: 'circle',
                  attrs: {
                    class: 'add',
                  },
                },
                {
                  tagName: 'text',
                  attrs: {
                    class: 'add',
                  },
                },
              ],
            },
            {
              tagName: 'g',
              attrs: {
                class: 'btn del',
              },
              children: [
                {
                  tagName: 'circle',
                  attrs: {
                    class: 'del',
                  },
                },
                {
                  tagName: 'text',
                  attrs: {
                    class: 'del',
                  },
                },
              ],
            },
            {
              tagName: 'g',
              attrs: {
                class: 'btn replace',
              },
              children: [
                {
                  tagName: 'circle',
                  attrs: {
                    class: 'add',
                  },
                },
                {
                  tagName: 'svg',
                  attrs: {
                    viewBox: '0 0 1137 1024',
                    width: 12,
                    height: 12
                  },
                  children: [
                    {
                      tagName: 'path',
                      attrs: {
                        d: 'M805.88195 284.333055c0.340989-5.683149 0.795641-11.650455 1.307125-17.901919l4.035035-240.965514a25.744665 25.744665 0 0 1 16.651627-23.357742 25.290013 25.290013 0 0 1 27.676935 6.819779l276.030542 319.336137c5.683149 7.61542 6.706116 17.845088 2.67108 26.483474a25.460507 25.460507 0 0 1-21.993786 14.719355h-192.886074a42.850943 42.850943 0 0 1-10.456994 1.250293H43.032872A43.078269 43.078269 0 0 1 0.068267 327.524986c0-23.869225 19.209043-43.191932 42.964605-43.191931h762.849078z m-480.794397 370.20032h767.963912c23.698731 0 42.964606 19.322706 42.964605 43.191931s-19.265875 43.191932-42.964605 43.191932H330.827533l-1.250293 16.651626-4.035035 240.965514a25.744665 25.744665 0 0 1-16.651627 23.357742 25.290013 25.290013 0 0 1-27.676935-6.819779L5.183101 695.679373a25.971991 25.971991 0 0 1-2.67108-26.483474 25.460507 25.460507 0 0 1 21.993786-14.719356H325.144384z',
                        fill: '#414F58'
                      },
                    },
                    {
                      tagName: 'text',
                      attrs: {
                        class: 'del',
                      },
                    },
                  ],
                },
              ],
            },
          ],
          attrs: {
            '.card': {
              rx: 10,
              ry: 10,
              refWidth: '100%',
              refHeight: '100%',
              stroke: '#5F95FF',
              fill: '#EFF4FF',
              strokeWidth: 1,
              pointerEvents: 'visiblePainted',
            },
            '.line_1': {
              fill: '#262626',
              refX: 10,// 与左侧的边框的距离
              refY: 32,
              textAnchor: 'start',/// 左对齐
            },
            '.line_2': {
              refX: 10,
              refY: 52,
              fill: '#262626',
              textAnchor: 'start',/// 左对齐
            },
            '.line_3': {
              refX: 10,
              refY: 72,
              fill: '#262626',
              textAnchor: 'start',/// 左对齐
            },
            '.line_4': {
              refX: 10,
              refY: 92,
              fill: '#262626',
              textAnchor: 'start',/// 左对齐
            },
            '.btn.add': {
              refDx: -16,
              refY: 16,
              event: 'node:add',
              cursor: "pointer",
              title: '增加子节点',// 鼠标悬浮提示
            },
            '.btn.del': {
              refDx: -44,
              refY: 16,
              event: 'node:delete',
              cursor: "pointer",
              title: '删除操作',// 鼠标悬浮提示
            },
            '.btn.replace': {
              refDx: -72,
              refY: 16,
              event: 'node:replace',
              cursor: "pointer",
              title: '替换当前节点',// 鼠标悬浮提示
            },
            '.btn > circle': {
              r: 10,
              fill: 'transparent',
              stroke: '#000',
              strokeWidth: 1,
              cursor: 'pointer',
            },
            '.btn.add > text': {
              fontSize: 20,
              fontWeight: 800,
              fill: '#000',
              x: -5.5,
              y: 7,
              fontFamily: 'Times New Roman',
              text: '+',
            },
            '.btn.del > text': {
              fontSize: 28,
              fontWeight: 500,
              fill: '#000',
              x: -4.5,
              y: 6,
              fontFamily: 'Times New Roman',
              text: '-',
            },
            '.btn.replace > svg': {
              fontSize: 12,
              fontWeight: 500,
              fill: '#000',
              x: -6,
              y: -6,
              fontFamily: 'Times New Roman',
            },
          },
        },
        true,
      )

      // 自定义边
      Graph.registerEdge(
        'org-edge',
        {
          zIndex: -1,
          inherit: 'edge',
          attrs: {
            line: {
              strokeWidth: 2,
              stroke: '#A2B1C3',
              // sourceMarker: null,
              // sourceMarker: 'block', // 实心箭头
              targetMarker: {
                name: 'classic'
              },
            },
          },
        },
        true,
      );

      // 布局方向
      const dir = 'LR' // LR RL TB BT

      // 创建画布
      graph = new Graph({
        container: document.getElementById('container'),
        autoResize: true,
        // width: window.innerWidth,
        // height: window.innerHeight,
        // width: 500,
        // height: 500,
        // scroller: true,// 允许滚动
        scroller: {
          enabled: true,// 允许滚动
          pannable: true,// 启用画布平移能力
        },
        interacting: false,
        // interacting: function (cellView) {
        //   return false;// 禁止节点拖拽 https://blog.csdn.net/qq_43274430/article/details/125763687
        // },
        connecting: {
          router: 'orth',// 连线的方式 ['normal','orth','oneSide','manhattan','metro','er']
        },
      })

      // 自动布局
      function CustomRender() {
        const nodes = graph.getNodes();
        const edges = graph.getEdges();
        const g = new dagre.graphlib.Graph();
        g.setGraph({
          rankdir: dir,
          // edgesep: 20, // 边间距
          nodesep: 60, // 节点间距
          ranksep: 50,// 层间距
        });
        g.setDefaultEdgeLabel(() => ({}));

        const width = BLOCK_WIDTH;
        const height = BLOCK_HEIGHT;
        nodes.forEach((node) => {
          g.setNode(node.id, { width, height })
        })

        edges.forEach((edge) => {
          const source = edge.getSource()
          const target = edge.getTarget()
          g.setEdge(source.cell, target.cell)
        })

        dagre.layout(g)

        g.nodes().forEach((id) => {
          const node = graph.getCellById(id);
          if (node) {
            const pos = g.node(id)
            node.position(pos.x, pos.y)
          }
        })

        edges.forEach((edge) => {
          const source = edge.getSourceNode()
          const target = edge.getTargetNode()
          const sourceBBox = source.getBBox()
          const targetBBox = target.getBBox()

          if ((dir === 'LR' || dir === 'RL') && sourceBBox.y !== targetBBox.y) {
            const gap =
              dir === 'LR'
                ? targetBBox.x - sourceBBox.x - sourceBBox.width
                : -sourceBBox.x + targetBBox.x + targetBBox.width
            const fix = dir === 'LR' ? sourceBBox.width : 0
            const x = sourceBBox.x + fix + gap / 2
            edge.setVertices([
              { x, y: sourceBBox.center.y },
              { x, y: targetBBox.center.y },
            ])
          } else if (
            (dir === 'TB' || dir === 'BT') &&
            sourceBBox.x !== targetBBox.x
          ) {
            const gap =
              dir === 'TB'
                ? targetBBox.y - sourceBBox.y - sourceBBox.height
                : -sourceBBox.y + targetBBox.y + targetBBox.height
            const fix = dir === 'TB' ? sourceBBox.height : 0
            const y = sourceBBox.y + fix + gap / 2
            edge.setVertices([
              { x: sourceBBox.center.x, y },
              { x: targetBBox.center.x, y },
            ])
          } else {
            edge.setVertices([])
          }
        })
      }

      function lineTextFn(text = '') {
        return Dom.breakText(text, {
          width: (BLOCK_WIDTH - 2 * BLOCK_PADDING) * 0.75,
          height: 20,
        }, {
          'font-size': 12
        }, {
          ellipsis: true
        })
      }

      function createNode(obj = {}) {
        return graph.createNode({
          id: obj.id,
          shape: 'org-node',
          data: obj,// 绑定传递的数据,以方便cell.getData()接收
          attrs: {
            '.line_1': {
              text: lineTextFn(`[${obj.seq}] ${obj.name}`),
              title: `${obj.name}`,// 添加鼠标悬浮提示
            },
            '.line_2': {
              text: lineTextFn(`负责人：${obj.member}`),
              // title: `${obj.member}`
            },
            '.line_3': {
              text: lineTextFn(`计划完成日期：${obj.time_start}`),
              // title: `${obj.time_start}`
            },
            '.line_4': {
              text: lineTextFn(`实际完成日期：${obj.time_end}`),
              // title: `${obj.time_end}`
            },
          },
        })
      }

      function createEdge(source, target) {
        return graph.createEdge({
          shape: 'org-edge',
          source: { cell: source },
          target: { cell: target },
        })
      }

      // 监听自定义事件
      function setup() {
        graph.on('node:add', ({ e, node }) => {
          e.stopPropagation()
          // const member = createNode(
          //   'Employee',
          //   'New Employee',
          //   Math.random() < 0.5 ? male : female,
          // )
          // graph.freeze()
          // graph.addCell([member, createEdge(node, member)])
          // CustomRender()
        })

        graph.on('node:delete', ({ e, node }) => {
          e.stopPropagation();
          // graph.freeze()
          // graph.removeCell(node)
          // CustomRender()
        })

        graph.on('node:replace', ({ e, node, cell }) => {
          e.stopPropagation();
          const nodeData = cell.getData();
          console.log(`节点数据`, nodeData);
          // console.log(`11-add.html 434 [replace]`, 'replace');
          // graph.freeze()
          // graph.removeCell(node)
          // CustomRender()
        })
      }

      const nodes = [
        createNode({
          id: 1,
          name: '前道测试',
          name: '前道测试前道测试前道测试前道测试前道测试前道测试',
          seq: '201A',
          member: '张一',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
        createNode({
          id: 2,
          name: '前道测试',
          seq: '201A',
          member: '张二',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
        createNode({
          id: 3,
          name: '前道测试',
          seq: '201A',
          member: '张三',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
        createNode({
          id: 4,
          name: '前道测试',
          seq: '201A',
          member: '张四',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
        createNode({
          id: 5,
          name: '前道测试',
          seq: '201A',
          member: '张五',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
        createNode({
          id: 6,
          name: '前道测试',
          seq: '201A',
          member: '张六',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
        createNode({
          id: 7,
          name: '前道测试',
          seq: '201A',
          member: '张七',
          time_start: '2023-10-19',
          time_end: '2023-10-19',
        }),
      ];

      const edges = [
        createEdge('1', '2'),
        createEdge('2', '3'),
        createEdge('2', '4'),
        createEdge('1', '5'),
        createEdge('5', '6'),
        createEdge('3', '6'),
        createEdge('4', '6'),
      ]

      graph.resetCells([...nodes, ...edges]);
      CustomRender();
      graph.centerContent(); // 移动视口中心点到内容区中心
      console.log(`10-nodeRender.html 404 [graph]`, graph);
      setup();
    }
  </script>
</body>

</html>